﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpGroupManager.h"
#include "IDocument.h"
#include "IGroupType.h"
#include "IGroup.h"
#include "IElementBasicInformation.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

GroupManager& GroupManager::GetInstance(IDocument* pDoc)
{
    static GroupManager instance;
    instance.m_pDocument = pDoc;
    return instance;
}

GroupManager::GroupManager()
{
    m_pDocument = nullptr;
}

GroupManager::~GroupManager()
{
}

ElementId GroupManager::GetRelativeElementIdInGroupType(IDocument* pDoc, const IGroup* pParentGroup, const ElementId& groupId, const ElementId& elementId) const
{
    DBG_WARN_AND_RETURN_UNLESS(pDoc, ElementId::InvalidID, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_UNLESS(pParentGroup, ElementId::InvalidID, L"pParentGroup为空",L"GDMPLab",L"2024-03-30");

    const std::map<ElementId, ElementId>& groupToTypeIdMap = pParentGroup->GetGroupToTypeIdMap();
    auto iter = groupToTypeIdMap.find(groupId);
    DBG_WARN_AND_RETURN_UNLESS(iter != groupToTypeIdMap.cend(), ElementId::InvalidID, L"查找组的映射关系失败",L"GDMPLab",L"2024-03-30");

    IGroup* pGroupInType = quick_cast<IGroup>(m_pDocument->GetElement(iter->second));
    DBG_WARN_AND_RETURN_UNLESS(pGroupInType, ElementId::InvalidID, L"pGroupInType为空",L"GDMPLab",L"2024-03-30");
    IGroup* pGroupInGroup = quick_cast<IGroup>(m_pDocument->GetElement(iter->first));
    DBG_WARN_AND_RETURN_UNLESS(pGroupInGroup, ElementId::InvalidID, L"pGroupInGroup为空",L"GDMPLab",L"2024-03-30");


    const std::map<ElementId, ElementId>& subGroupToTypeIdMap = pGroupInGroup->GetGroupToTypeIdMap();
    const std::map<ElementId, ElementId>& subTypeToGroupIdMap = pGroupInType->GetTypeToGroupIdMap();
    auto subItemInGroup = subGroupToTypeIdMap.find(elementId);
    DBG_WARN_AND_RETURN_UNLESS(subItemInGroup != subGroupToTypeIdMap.cend(), ElementId::InvalidID, L"查找映射关系失败",L"GDMPLab",L"2024-03-30");
    auto subItemInType = subTypeToGroupIdMap.find(subItemInGroup->second);
    DBG_WARN_AND_RETURN_UNLESS(subItemInType != subTypeToGroupIdMap.cend(), ElementId::InvalidID, L"查找映射关系失败",L"GDMPLab",L"2024-03-30");

    return subItemInType->second;
}

void GroupManager::SetSync(const gcmp::ElementId& groupId, const gcmp::ElementId& elementId)
{
    auto groupIter = m_unsyncGroupElementRecord.find(groupId);
    if (groupIter == m_unsyncGroupElementRecord.end())
        return;

    DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDocument, L"m_pDocument为空",L"GDMPLab",L"2024-03-30");
    const IGroup* pGroup = quick_cast<const IGroup>(m_pDocument->GetElement(groupId));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGroup, L"pGroup为空",L"GDMPLab",L"2024-03-30");

    if (pGroup->GetGroupId().IsValid())
    {
        IGroup* pParentGroup = quick_cast<IGroup>(m_pDocument->GetElement(pGroup->GetGroupId()));
        if (pParentGroup)
        {
            RemoveReferenceElement(pParentGroup, groupId, elementId);

            const IGroup* pSubGroup = quick_cast<const IGroup>(m_pDocument->GetElement(elementId));
            if (pSubGroup)
            {
                UnorderedElementIdSet elementIdsInSubGroup;
                pSubGroup->GetAllElementIdsInGroup(elementIdsInSubGroup);
                FOR_EACH(elementIdInSubGroup, elementIdsInSubGroup)
                {
                    if (IsSync(elementId, elementIdInSubGroup) == false)
                    {
                        AddReferenceElement(pGroup, elementId, elementIdInSubGroup);
                    }
                }
            }
        }
    }

    groupIter->second.erase(elementId);

    if (groupIter->second.empty())
    {
        m_unsyncGroupElementRecord.erase(groupId);
    }
    
}

void GroupManager::SetSync(const gcmp::ElementId& groupId, const std::vector<gcmp::ElementId>& elementIds)
{
    for (const auto& elementId : elementIds)
        SetSync(groupId, elementId);
}

IGroupType* GroupManager::GetGroupType(const IGroup* pGroup) const
{
    if (!pGroup)
        return nullptr;

    const IElementBasicInformation* pBasicInfo = pGroup->GetBasicInformation();
    if (!pBasicInfo)
        return nullptr;

    return quick_cast<IGroupType>(pBasicInfo->GetType());
}

void GroupManager::AddReferenceElement(const IGroup* pParentGroup, const ElementId& groupId, const ElementId& elementIdInGroup) const
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDocument, L"m_pDocument为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pParentGroup, L"pParentGroup为空",L"GDMPLab",L"2024-03-30");

    ElementId elementIdInType = GetRelativeElementIdInGroupType(m_pDocument, pParentGroup, groupId, elementIdInGroup);
    DBG_WARN_AND_RETURN_VOID_UNLESS(elementIdInType.IsValid(), L"elementIdInType为无效值",L"GDMPLab",L"2024-03-30");

    IGroupType* pGroupType = GetGroupType(pParentGroup);
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGroupType, L"pGroupType为空",L"GDMPLab",L"2024-03-30");

    pGroupType->AddReferenceElement(elementIdInType);

}

void GroupManager::RemoveReferenceElement(const IGroup* pParentGroup, const ElementId& groupId, const ElementId& elementIdInGroup) const
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDocument, L"m_pDocument为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pParentGroup, L"pParentGroup为空",L"GDMPLab",L"2024-03-30");

    ElementId elementIdInType = GetRelativeElementIdInGroupType(m_pDocument, pParentGroup, groupId, elementIdInGroup);
    DBG_WARN_AND_RETURN_VOID_UNLESS(elementIdInType.IsValid(), L"elementIdInType为无效值",L"GDMPLab",L"2024-03-30");

    IGroupType* pGroupType = GetGroupType(pParentGroup);
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGroupType, L"pGroupType为空",L"GDMPLab",L"2024-03-30");

    pGroupType->RemoveReferenceElement(elementIdInType);

}

void GroupManager::SetUnsync(const gcmp::ElementId& groupId, const gcmp::ElementId& elementId)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDocument, L"m_pDocument为空",L"GDMPLab",L"2024-03-30");
    IGroup* pGroup = quick_cast<IGroup>(m_pDocument->GetElement(groupId));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGroup, L"pGroup为空",L"GDMPLab",L"2024-03-30");

    if (pGroup->GetGroupId().IsValid() && IsSync(pGroup->GetGroupId(), groupId))
    {
        IGroup* pParentGroup = quick_cast<IGroup>(m_pDocument->GetElement(pGroup->GetGroupId()));
        if (pParentGroup)
        {
            AddReferenceElement(pParentGroup, groupId, elementId);

            const IGroup* pSubGroup = quick_cast<const IGroup>(m_pDocument->GetElement(elementId));
            if (pSubGroup)
            {
                UnorderedElementIdSet elementIdsInSubGroup;
                pSubGroup->GetAllElementIdsInGroup(elementIdsInSubGroup);
                FOR_EACH(elementIdInSubGroup, elementIdsInSubGroup)
                {
                    if (pGroup->IsReferenceElementInGroup(elementIdInSubGroup))
                    {
                        RemoveReferenceElement(pGroup, elementId, elementIdInSubGroup);
                    }
                }
            }
        }
    }

    auto result = m_unsyncGroupElementRecord.insert(std::make_pair(groupId, std::set<gcmp::ElementId>()));
    result.first->second.insert(elementId);
}

void GroupManager::SetUnsync(const gcmp::ElementId& groupId, const std::vector<gcmp::ElementId>& elementIds)
{
    for (const auto& elementId : elementIds)
        SetUnsync(groupId, elementId);
}

bool GroupManager::IsSync(const gcmp::ElementId& groupId, const gcmp::ElementId& elementId) const
{
    auto groupIter = m_unsyncGroupElementRecord.find(groupId);
    if (groupIter == m_unsyncGroupElementRecord.end())
        return true;

    auto elementIter = groupIter->second.find(elementId);
    if (elementIter == groupIter->second.end())
        return true;

    return false;
}

bool GroupManager::IsUnsync(const gcmp::ElementId& groupId, const gcmp::ElementId& elementId) const
{
    return !IsSync(groupId, elementId);
}
